package erlwitch.parser;

import java.io.Reader;
import java_cup.runtime.Symbol;
import java_cup.runtime.ComplexSymbolFactory;

%%
%class ErlangScanner
%public
%unicode
%line
%column
%char
%init{
	symbolFactory = new ComplexSymbolFactory();
%init}

%cup
%eofval{
	return symbolFactory.newSymbol("EOF",Sym.EOF);
%eofval}

%{
	private StringBuffer string = new StringBuffer();
	private ComplexSymbolFactory symbolFactory;	

	public ErlangScanner(Reader reader, ComplexSymbolFactory sf){
		this(reader);
		symbolFactory = sf;
	}

	private Symbol symbol(String name, int type) {
		Symbol s = symbolFactory.newSymbol(name, type);
		s.left = yychar;
		s.right = yychar + yylength();
		return s;
	}
	private Symbol symbol(String name, int type, Object value) {
		Symbol s = symbolFactory.newSymbol(name, type, value);
		s.left = yychar;
		s.right = yychar + yylength();
		return s;
	}
	
	public boolean isEOF() {
		return yy_atEOF;
	}
%}

LineTerminator = \r|\n|\r\n
InputCharacter = [^\r\n]
WhiteSpace     = {LineTerminator} | [ \t\f]

/* comments */
Comment = "%".* {LineTerminator}

Identifier = [:jletter:] ([:jletterdigit:]|"@")*

DecIntegerLiteral = [0-9]+
BaseIntegerLiteral = {DecIntegerLiteral}#[:jletterdigit:]*
SimpleFloatLiteral = {DecIntegerLiteral} "\." {DecIntegerLiteral}
FloatLiteral = {SimpleFloatLiteral} | {SimpleFloatLiteral} "e" {DecIntegerLiteral}
%state STRING
%state LONG_ATOM

%%

/* keywords */

<YYINITIAL> {
  /* reserved words */
	after        {return symbol("AFTER", Sym.AFTER, yytext());}
	and          {return symbol("AND", Sym.AND, yytext());}
	andalso      {return symbol("ANDALSO", Sym.ANDALSO, yytext());}
	band         {return symbol("BAND", Sym.BAND, yytext());}
	begin        {return symbol("BEGIN", Sym.BEGIN, yytext());}
	bnot         {return symbol("BNOT", Sym.BNOT, yytext());}
	bor          {return symbol("BOR", Sym.BOR, yytext());}
	bsl          {return symbol("BSL", Sym.BSL, yytext());}
	bsr          {return symbol("BSR", Sym.BSR, yytext());}
	bxor         {return symbol("BXOR", Sym.BXOR, yytext());}
	case         {return symbol("CASE", Sym.CASE, yytext());}
	catch        {return symbol("CATCH", Sym.CATCH, yytext());}
/*	cond         {return symbol("COND", Sym.COND, yytext());}*/
	div          {return symbol("DIV", Sym.DIV, yytext());}
	end          {return symbol("END", Sym.END, yytext());}
	fun          {return symbol("FUN", Sym.FUN, yytext());}
	if           {return symbol("IF", Sym.IF, yytext());}
/*	let          {return symbol("LET", Sym.LET, yytext());}*/
	not          {return symbol("NOT", Sym.NOT, yytext());}
	of           {return symbol("OF", Sym.OF, yytext());}
	or           {return symbol("OR", Sym.OR, yytext());}
	orelse       {return symbol("ORELSE", Sym.ORELSE, yytext());}
	query        {return symbol("QUERY", Sym.QUERY, yytext());}
	receive      {return symbol("RECEIVE", Sym.RECEIVE, yytext());}
	rem          {return symbol("REM", Sym.REM, yytext());}
	try          {return symbol("TRY", Sym.TRY, yytext());}
	when         {return symbol("WHEN", Sym.WHEN, yytext());}
	xor          {return symbol("XOR", Sym.XOR, yytext());}
  
  /* identifiers */ 
  {Identifier}                   { char c = yytext().charAt(0);
                                   if (Character.isUpperCase(c) || c == '_') {
                                      return symbol("VAR", Sym.VAR, yytext());
                                   } else {
                                      return symbol("ATOM", Sym.ATOM, yytext());
                                   }
                                 }
 
  /* literals */
  "$" ("\\".|.)			 		 { return symbol("CHAR", Sym.CHAR, yytext()); }
  {DecIntegerLiteral}            { return symbol("INTEGER", Sym.INTEGER, yytext()); }
  {BaseIntegerLiteral}           { return symbol("INTEGER", Sym.INTEGER, yytext()); }
  {FloatLiteral}                 { return symbol("FLOAT", Sym.FLOAT, yytext()); }
  \"                             { string.setLength(0); yybegin(STRING); }
  \'                             { string.setLength(0); yybegin(LONG_ATOM); }

  /* arithmetic operators */
  "+"                            { return symbol("PLUS", Sym.PLUS); }
  "-"                            { return symbol("MINUS", Sym.MINUS); }
  "*"                            { return symbol("STAR", Sym.STAR); }
  "/"                            { return symbol("SLASH", Sym.SLASH); }

  /* boolean operators */
  "="                            { return symbol("EQ", Sym.EQ); }
  "=="                           { return symbol("EQEQ", Sym.EQEQ); }
  "<"                            { return symbol("LT", Sym.LT); }
  "=<"                           { return symbol("LTEQ", Sym.LTEQ); }
  ">"                            { return symbol("GT", Sym.GT); }
  ">="                           { return symbol("GTEQ", Sym.GTEQ); }
  "/="                           { return symbol("NOTEQ", Sym.NOTEQ); }
  "=:="                          { return symbol("IDEN", Sym.IDEN); }
  "=/="                          { return symbol("NOTIDEN", Sym.NOTIDEN); }
  
  /* list operators */
  "++"                           { return symbol("APPEND", Sym.APPEND); }
  "--"                           { return symbol("SUBSTRACT", Sym.SUBSTRACT); }

  /* parenthesis */
  "("							 { return symbol("PAREN_OPEN", Sym.PAREN_OPEN); }
  ")"							 { return symbol("PAREN_CLOSE", Sym.PAREN_CLOSE); }
  "["							 { return symbol("BRACKET_OPEN", Sym.BRACKET_OPEN); }
  "]"							 { return symbol("BRACKET_CLOSE", Sym.BRACKET_CLOSE); }
  "{"							 { return symbol("CURLY_OPEN", Sym.CURLY_OPEN); }
  "}"							 { return symbol("CURLY_CLOSE", Sym.CURLY_CLOSE); }
 "<<"                            { return symbol("BIN_OPEN", Sym.BIN_OPEN, yytext());}
 ">>"                            { return symbol("BIN_CLOSE", Sym.BIN_CLOSE, yytext());}

  /* comments */
  {Comment}                      { /* ignore */ }
 
  /* whitespace */
  {WhiteSpace}                   { /* ignore */ }
}

<STRING> {
  \"                             { yybegin(YYINITIAL); 
                                   return symbol("STRING", Sym.STRING, 
                                   string.toString()); }
  [^\"\\]+                       { string.append(yytext()); }
  \\t                            { string.append('\t'); }
  \\n                            { string.append('\n'); }

  \\r                            { string.append('\r'); }
  \\\"                           { string.append('\"'); }
  \\\\                           { string.append('\\'); }
}

<LONG_ATOM> {
  \'                             { yybegin(YYINITIAL); 
                                   return symbol("ATOM", Sym.ATOM, 
                                   string.toString()); }
  [^\'\\]+                       { string.append(yytext()); }
  \\t                            { string.append('\t'); }
  \\n                            { string.append('\n'); }

  \\r                            { string.append('\r'); }
  \\\'                           { string.append('\''); }
  \\\\                           { string.append('\\'); }
}

  /* function definition */
/*Fin de comando*/ 									
 "." {WhiteSpace}* ({LineTerminator}|{Comment})
 								 { return symbol("PERIOD", Sym.PERIOD, yytext());}
/*Cualquier punto que aparezca*/ 								 
 "."                         	 { return symbol("DOT", Sym.DOT, yytext());} /*no se donde mas poner este caracter. Lo pongo aca abajo para poder reconocer floats*/
 ","                             { return symbol("COMMA", Sym.COMMA, yytext());}
 ";"                             { return symbol("SEMICOLON", Sym.SEMICOLON, yytext());}
 ":"                             { return symbol("COLON", Sym.COLON, yytext());}
 "|"                             { return symbol("PIPE", Sym.PIPE, yytext());}
 "||"                            { return symbol("DOUBLE_PIPE", Sym.PIPE, yytext());}
 "->"                            { return symbol("FUN_DEF", Sym.FUN_DEF); }
 "<-"                            { return symbol("IN_DEF", Sym.IN_DEF); }
 "_"                             { return symbol("ANONYMOUS", Sym.ANONYMOUS); }
 "!"                             { return symbol("SEND", Sym.SEND); }
 "#"                             { return symbol("SHARP", Sym.SHARP, yytext());}
 "?"                             { return symbol("MACRO", Sym.MACRO, yytext());}
 "<="                            { return symbol("LEFT_ARROW", Sym.LEFT_ARROW, yytext());}
 "::"                            { return symbol("DOUBLE_COLON", Sym.DOUBLE_COLON, yytext());}

/* error fallback */
.|\n                             { throw new Error("Illegal character <"+yytext()+">"); }
